﻿/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#ifndef UGAME_BINARY_BYTEBUFFER_H_
#define UGAME_BINARY_BYTEBUFFER_H_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include <UGame/Exception.h>
#include <UGame/Macros.h>
#include <UGame/Util/ByteConverter.h>

namespace UGame {
namespace Binary {

		class ByteBufferException : public Exception
		{
		public:
			ByteBufferException(bool _Add, ::std::size_t _Pos,
				::std::size_t _Size, ::std::size_t _Esize)
				: _add(_Add), _pos(_Pos), _size(_Size), _eSize(_Esize)
			{
				_PrintPosError();
			}

			const char* what(void) const throw ();

		private:
			void _PrintPosError() const;

            bool            _add;       /* Read or write */
            std::size_t     _pos;       /* Operation position */
            std::size_t     _size;      /* Current cache size */
            std::size_t     _eSize;     /* Operation size */

			mutable std::string     _what;
		};

		inline const char *ByteBufferException::what() const throw()
		{
			return _what.c_str();
		}


		class ByteBuffer
		{
		public:
			const static std::size_t DEFAULT_SIZE = 0x1000;   /* Default cache size */
			const static std::size_t MAX_SIZE = 0x100000;     /* Max cache size */

			typedef std::vector<std::uint8_t>::size_type size_type;

		public:
			ByteBuffer(void)
				: _rpos(0)
				, _wpos(0)
				, _storage()
			{
				_storage.reserve(DEFAULT_SIZE);
			}

			explicit ByteBuffer(size_type _Size)
				: _rpos(0)
				, _wpos(0)
			{
				_storage.reserve(_Size);
			}

			ByteBuffer(const ByteBuffer &_Buf)
				: _rpos(_Buf._rpos)
				, _wpos(_Buf._wpos)
				, _storage(_Buf._storage)
			{

			}

			virtual ~ByteBuffer(void)
			{

			}

			void Clear()
			{
				_storage.clear();
				_rpos = _wpos = 0;
			}

            void Resize(size_type _Nsize, bool _Wnew = false)
			{
				_storage.resize(_Nsize);
				_rpos = 0;
                _wpos = _Wnew ? _storage.size() : 0;
			}

			size_type size(void) const
			{
				return _storage.size();
			}

			const std::uint8_t* contents(size_type _Index = 0) const
			{
				UASSERT(_Index >= 0 && _Index < size());
				return &_storage[_Index];
			}
			std::uint8_t* contents(size_type _Index = 0)
			{
				UASSERT(_Index >= 0 && _Index < size());
				return &_storage[_Index];
			}

			size_type rpos(void) const {
				return _rpos;
			}
			size_type wpos(void) const {
				return _wpos;
			}

			void rpos(size_type _Pos) {
				_rpos = _Pos;
			}
			void wpos(size_type _Pos) {
				_wpos = _Pos;
			}

		public:
			///////////////////////////////////////////////////////////////////
			ByteBuffer& operator<<(std::uint8_t _X)
			{
				Append<std::uint8_t>(_X);
				return *this;
			}
			ByteBuffer& operator<<(std::uint16_t _X)
			{
				Append<std::uint16_t>(_X);
				return *this;
			}
			ByteBuffer& operator<<(std::uint32_t _X)
			{
				Append<std::uint32_t>(_X);
				return *this;
			}
			ByteBuffer& operator<<(std::uint64_t _X)
			{
				Append<std::uint64_t>(_X);
				return *this;
			}
			ByteBuffer& operator<<(float _X)
			{
				Append(_X);
				return *this;
			}
			ByteBuffer& operator<<(const char* _X)
			{
				Append(_X, strlen(_X));
				*this << (std::uint8_t)0;
				return *this;
			}
			ByteBuffer& operator<<(const std::string& _X)
			{
				Append(_X.c_str(), _X.length());
				*this << (std::uint8_t)0;
				return *this;
			}
			///////////////////////////////////////////////////////////////////
			ByteBuffer& operator>>(std::uint8_t& _X)
			{
				_X = Read<std::uint8_t>();
				return *this;
			}
			ByteBuffer& operator>>(std::uint16_t& _X)
			{
				_X = Read<std::uint16_t>();
				return *this;
			}
			ByteBuffer& operator>>(std::uint32_t& _X)
			{
				_X = Read<std::uint32_t>();
				return *this;
			}
			ByteBuffer& operator>>(std::uint64_t& _X)
			{
				_X = Read<std::uint64_t>();
				return *this;
			}
			ByteBuffer& operator>>(std::string& _Value)
			{
				_Value.clear();
				while (_rpos < size())
				{
					char c = Read<char>();
					if (c == 0)
					{
						break;
					}
					_Value += c;
				}
				return *this;
			}
			///////////////////////////////////////////////////////////////////
			std::uint8_t operator[](size_type _Index)
			{
				return (_storage[_Index]);
			}

			void Append(const char* _Src, size_type _Size)
			{
				if (_Size == 0) {
					return;
				}

				if ((_Size + _wpos) > MAX_SIZE) {
					throw ByteBufferException(true, _wpos, size(), _Size);
				}

				if (size() < (_Size + _wpos)) {
					_storage.resize(_Size + _wpos);
				}

				memcpy(&_storage[_wpos], _Src, _Size);
				_wpos += _Size;
			}

			void Append(const std::uint8_t* _Src, size_type _Size)
			{
				Append((const char*)_Src, _Size);
			}

			void Append(const ByteBuffer& _Bbuf)
			{
				Append(_Bbuf.contents(), _Bbuf.size());
			}

			void Read(std::uint8_t* _Dest, size_type _Size)
			{
				if ((_rpos + _Size) > size())
				{
					throw ByteBufferException(false, _rpos, size(), _Size);
				}
				memcpy(_Dest, &_storage[_rpos], _Size);
				_rpos += _Size;
			}

			template<typename T>
			void Put(size_t _Pos, T& _X)
			{
				Util::endianConvert(_X);
				Put(_Pos, (std::uint8_t*)&_X, sizeof(T));
			}

			void Put(size_type _Pos, std::uint8_t *_Ptr, std::size_t _Size)
			{
				if ((_Pos + _Size) > size()) {
					throw ByteBufferException(true, _Pos, _Size, size());
				}
				memcpy(&_storage[_Pos], _Ptr, _Size);
			}

			template <typename T> T Read(void)
			{
				T _R = Read<T>(_rpos);
				_rpos += sizeof(T);
				return _R;
			}

            void Dump(std::string& _Str);

		protected:
			template <typename T> T Read(size_t _Pos) const
			{
				if (_Pos + sizeof(T) > size()) {
					throw ByteBufferException(false, _Pos, size(), sizeof(T));
				}
				T _Val = *((T const*)&_storage[_Pos]);
				Util::endianConvert(_Val);
				return _Val;
			}

			template<typename T>
			void Append(T _X)
			{
				Append((const char*)&_X, sizeof(T));
			}

		protected:
            size_type                       _rpos;      /* Current read position */
            size_type                       _wpos;      /* Current write position */
            std::vector<std::uint8_t>       _storage;   /* cache */
		};
}}

#endif /* UGAME_BINARY_BYTEBUFFER_H_ */
